//===--- ClangNode.h - How Swift tracks imported Clang entities -*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef POLARPHP_AST_CLANGNODE_H
#define POLARPHP_AST_CLANGNODE_H

#include "llvm/ADT/PointerUnion.h"

namespace clang {
class Decl;
class MacroInfo;
class ModuleMacro;
class Module;
class SourceLocation;
class SourceRange;
}

namespace polar {

namespace internal {
/// A wrapper to avoid having to import Clang headers. We can't just
/// forward-declare their PointerLikeTypeTraits because we don't own
/// the types.
template <typename T>
struct ClangNodeBox {
   const T * const value;

   ClangNodeBox() : value{nullptr} {}
   /*implicit*/ ClangNodeBox(const T *V) : value(V) {}

   explicit operator bool() const { return value; }
};
}

/// Represents a clang declaration, macro, or module. A macro definition
/// imported from a module is recorded as the ModuleMacro, and a macro
/// defined locally is represented by the MacroInfo.
class ClangNode {
   template <typename T>
   using Box = internal::ClangNodeBox<T>;

   llvm::PointerUnion4<Box<clang::Decl>, Box<clang::MacroInfo>,
   Box<clang::ModuleMacro>, Box<clang::Module>> Ptr;

public:
   ClangNode() = default;
   ClangNode(const clang::Decl *D) : Ptr(D) {}
   ClangNode(const clang::MacroInfo *MI) : Ptr(MI) {}
   ClangNode(const clang::ModuleMacro *MM) : Ptr(MM) {}
   ClangNode(const clang::Module *Mod) : Ptr(Mod) {}

   bool isNull() const { return Ptr.isNull(); }
   explicit operator bool() const { return !isNull(); }

   const clang::Decl *getAsDecl() const {
      return Ptr.dyn_cast<Box<clang::Decl>>().value;
   }
   const clang::MacroInfo *getAsMacroInfo() const {
      return Ptr.dyn_cast<Box<clang::MacroInfo>>().value;
   }
   const clang::ModuleMacro *getAsModuleMacro() const {
      return Ptr.dyn_cast<Box<clang::ModuleMacro>>().value;
   }
   const clang::Module *getAsModule() const {
      return Ptr.dyn_cast<Box<clang::Module>>().value;
   }

   const clang::Decl *castAsDecl() const {
      return Ptr.get<Box<clang::Decl>>().value;
   }
   const clang::MacroInfo *castAsMacroInfo() const {
      return Ptr.get<Box<clang::MacroInfo>>().value;
   }
   const clang::ModuleMacro *castAsModuleMacro() const {
      return Ptr.get<Box<clang::ModuleMacro>>().value;
   }
   const clang::Module *castAsModule() const {
      return Ptr.get<Box<clang::Module>>().value;
   }

   // Get the MacroInfo for a local definition, one imported from a
   // ModuleMacro, or null if it's neither.
   const clang::MacroInfo *getAsMacro() const;

   /// Returns the module either the one wrapped directly, the one from a
   /// clang::ImportDecl or null if it's neither.
   const clang::Module *getClangModule() const;

   clang::SourceLocation getLocation() const;
   clang::SourceRange getSourceRange() const;

   void *getOpaqueValue() const { return Ptr.getOpaqueValue(); }
   static inline ClangNode getFromOpaqueValue(void *VP) {
      ClangNode N;
      N.Ptr = decltype(Ptr)::getFromOpaqueValue(VP);
      return N;
   }
};

} // end namespace polar

namespace llvm {
template <typename T>
struct PointerLikeTypeTraits<polar::internal::ClangNodeBox<T>> {
   using Box = ::polar::internal::ClangNodeBox<T>;

   static inline void *getAsVoidPointer(Box P) {
      return const_cast<void *>(static_cast<const void *>(P.value));
   }
   static inline Box getFromVoidPointer(const void *P) {
      return Box{static_cast<const T *>(P)};
   }

   /// Note: We are relying on Clang nodes to be at least 4-byte aligned.
   enum { NumLowBitsAvailable = 2 };
};

} // end namespace llvm

#endif
